﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Reflection;

namespace Utils.Excel
{
    public abstract class ExcelBase<T> : IExcel<T>
    {

        public ExcelBase()
        {
            current = new List<T>();
            InitColumn();
        }
        public ExcelBase(Stream stream, Func<IDictionary<string, string>, T> func, string sheetName)
        {
            current = new List<T>();
            if (func != null)
                excelRead = (dict, index) => func(dict);
            InitFromExcel(stream, sheetName);
        }
        public ExcelBase(Stream stream, Func<IDictionary<string, string>, int, T> func, string sheetName)
        {
            current = new List<T>();
            if (func != null)
                excelRead = func;
            InitFromExcel(stream, sheetName);
        }


        protected abstract void InitFromExcel(Stream stream, string sheetName);

        protected Func<IDictionary<string, string>, int, T> excelRead;

        protected Dictionary<string, (PropertyInfo propertyInfo, Dictionary<string, string> styles)> columnProperty;

        protected List<T> current;


        public T this[int index] { get => current[index]; set => current[index] = value; }


        public IExcel<T> Add(T value)
        {
            current.Add(value);
            return this;
        }

        public IExcel<T> AddRange(IEnumerable<T> collection)
        {
            current.AddRange(collection);
            return this;
        }

        public IExcel<T> Clear()
        {
            current.Clear();
            return this;
        }


        public IExcel<T> Insert(int index, T value)
        {
            current.Insert(index, value);
            return this;
        }

        public IExcel<T> RemoveAt(int index)
        {
            current.RemoveAt(index);
            return this;
        }

        public List<T> ToList()
        {
            return current;
        }

        public string Title { get; set; }

        public abstract byte[] ToBytes();


        private void InitColumn()
        {
            var type = typeof(T);
            columnProperty = new Dictionary<string, (PropertyInfo, Dictionary<string, string>)>();

            PropertyInfo[] ps = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
            foreach (var p in ps)
            {
                if (!p.CanRead) continue;

                var description = p.GetCustomAttribute<DescriptionAttribute>()?.Description;

                var displayName = p.GetCustomAttribute<DisplayNameAttribute>();
                if (displayName == null) continue;
                columnProperty.Add(displayName.DisplayName ?? p.Name, (p, Resolve(description)));
            }
            if (columnProperty.Count == 0) throw new ArgumentException("必须包含可读字段");
        }


        private static Dictionary<string, string> Resolve(string description)
        {
            if (string.IsNullOrEmpty(description)) return null;

            Dictionary<string, string> dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            var list = description.Split(';');
            foreach (var item in list)
            {
                if (item.Contains("="))
                {
                    var sp = item.Split('=');
                    dict.Add(sp[0], item.Substring(sp[0].Length + 1));
                }
            }
            return dict;
        }


        protected bool TryGetValue(string valueIn, Type propertyType, out object valueOut)
        {
            valueOut = valueIn;
            bool beNull = false;
            if ("Nullable`1" == propertyType.Name)
            {
                beNull = true;
                propertyType = propertyType.GetGenericArguments()[0];
            }
            if (propertyType.GetInterface("IConvertible") == null)
            {
                return false;
            }
            if (propertyType.Equals(typeof(bool)))
            {
                valueOut = (valueIn != "否" && valueIn != "" && valueIn != "0" && !valueIn.Equals("false", StringComparison.OrdinalIgnoreCase));
            }
            try
            {
                valueOut = Convert.ChangeType(valueOut, propertyType);
                return true;
            }
            catch (Exception)
            {
                valueOut = null;
                return beNull;
            }
        }

        protected Func<IDictionary<string, string>, int, T> ToEntity()
        {
            var columnProperties = new Dictionary<string, PropertyInfo>();
            PropertyInfo[] ps = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
            foreach (var p in ps)
            {
                if (!p.CanWrite) continue;
                var displayName = p.GetCustomAttribute<DisplayNameAttribute>();
                columnProperties.Add(displayName == null ? p.Name : displayName.DisplayName, p);
            }
            return (dict, index) =>
            {
                var obj = Activator.CreateInstance<T>();
                foreach (var p in columnProperties)
                {
                    if (!dict.ContainsKey(p.Key))
                    {
                        continue;
                    }
                    if (TryGetValue(dict[p.Key], p.Value.PropertyType, out object value))
                    {
                        p.Value.SetValue(obj, value);
                    }
                    else
                    {
                        throw Error.DefaultError($"第{index}行 {p.Key}：无法转为{p.Value.PropertyType.Name}类型");
                    }
                }
                return obj;
            };
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return current.GetEnumerator();
        }

        public IEnumerator<T> GetEnumerator()
        {
            return current.GetEnumerator();
        }

    }
}
